<?php
require_once 'Constants.php';
require_once ('ClassGeneratorJavascript.php');
require_once 'Config.php';

/**
 * Class for generating the code for the MySQL classes
 *
 */
class ClassGenerator {

	/**
	 * The name of the class to generate
	 *
	 * @var string
	 */
	public $class_name;

	/**
	 * The title formatting of the class name (UC first letter, with spaces)
	 * @var string
	 */
	protected $class_title;
	
	/**
	 * The table to draw this class from
	 *
	 * @var string
	 */
	protected $table_name;
	

	/**
	 * The table from which to generate a class
	 *
	 * @var array
	 */
	protected $fields;

	/**
	 * A list of tables for which my primary key is their foreign key
	 *
	 * @var array
	 */
	protected $foreign_tables;

	/**
	 * The name of the database this class accesses
	 *
	 * @var string
	 */
	protected $database;

	/**
	 * Instructs formatClassName to ignore reformatting, useful for non-english databases
	 *
	 * @var bool
	 */
	private $ignore_class_name_reformatting;
	
	/**
	 * Constructs the class using the given table
	 *
	 * @param string $database
	 * @param string $table_name The name of the class to generate
	 * @param array $fields The table structure from which to generate a class
	 * @param array $foreign_tables The set of tables for which this class's primary key is a foreign key
	 * @param bool $ignore_class_name_reformatting Instructs formatClassName to ignore reformatting, useful for non-english databases
	 */
	function __construct($database, $table_name, $fields, $foreign_tables, $ignore_class_name_reformatting = false) {
		$this->database = $database;
		$this->ignore_class_name_reformatting = $ignore_class_name_reformatting;
		$this->class_name = $this->formatClassName ( $table_name );
		$this->table_name = $table_name;
		$this->class_title = $this->formatClassName ($table_name, false);
		$this->fields = $fields;
		$this->foreign_tables = $foreign_tables;
	}

	/**
	 * Formats the table_name into CamelCase as TableName and removes s's/ ies's
	 * @param string $table_name
	 * @return string The formatted class name
	 */
	protected function formatClassName($table_name, $removeSpaces = true) {		
		//replace underscores with spaces
		$className = str_ireplace ( "_", " ", $table_name );
		//upper case the first letter of each word
		$className = ucwords ( $className );
		
		if($removeSpaces){
			//get rid of the spaces
			$className = str_ireplace ( " ", "", $className );
		}	
			 
		//if they don't want reformatting, just return the original table name with caps, don't change plurals
		if(!$this->ignore_class_name_reformatting){
			if (EndsWith ( "ies", $className )) {
				//replace the ies with y
				$className = substr ( $className, 0, strlen ( $className ) - 3 );
				$className .= "y";
			} //				else //			//replace the ies with y
			//			$className = str_ireplace ( "Lists", "", $className );
			//		}
			else if(EndsWith("sses", $className)){
				//remove the es
				$className = substr ( $className, 0, strlen ( $className ) - 2 );
			}
			else if (EndsWith ( "s", $className )) {
				//remove the s
				$className = substr ( $className, 0, strlen ( $className ) - 1 );
			}
		}
		return $className;
	}

	/**
	 * Generates and returns the code for the class
	 *
	 * @return string The code for the PHP class
	 */
	public function generateClassCode() {
		$requirements = <<< AUTOLOAD
		
global \$madalDirs;
if(!isset(\$madalDirs)){
	\$madalDirs = array();
}
if(!in_array(dirname(__FILE__) . '/', \$madalDirs)){
	array_push(\$madalDirs, dirname(__FILE__) . '/');
}

if(!function_exists("__autoload")){
	//deals with loading the required classes for this object
	function __autoload(\$class_name) {
	    global \$madalDirs;
		if(\$class_name == 'FieldProperties.php' || \$class_name == 'FieldProperties'){
			require_once dirname(__FILE__).'/../Controller/Includes/FieldProperties.php';
		}
		else if(\$class_name == 'DBElement'){
			require_once dirname(__FILE__).'/Includes/DBElement.php';
		}
		//check the search list array
		else{
			foreach(\$madalDirs as \$dir){
				if(file_exists(\$dir . \$class_name . '.php')){
					require_once \$dir . \$class_name . '.php';
					return;
				}
			}
			//it's not in the list, throw an exception
			throw new Exception("file not found for " . \$class_name . '.php');
		}
	}
}
AUTOLOAD;

		$public_fields = "";
		$insert_new_comments = "";
		$insert_new_field_names = "";

		//loop through each of the fields
		foreach ( $this->fields as $field_name => $field_properties ) {
			$field_properties [FIELD_NAME_FIELD] = $field_name;

			//get the php data type
			$field_properties [DATA_TYPE_FIELD] = ConvertMySQLTypeNameToPHPTypeName ( $field_properties [DATA_TYPE_FIELD] );

			//check if this is the primary field
			$is_primary_field = false;
			if ($field_properties [INDEX_FIELD] == PRIMARY_FIELD) {
				$primary_key_field = $field_name;
				$primary_key_type = $field_properties [DATA_TYPE_FIELD];
				$is_primary_field = true;
			}
			else{
				$insert_new_comments .= "* @param ".$field_properties [DATA_TYPE_FIELD]." $field_name, " . $field_properties [COMMENTS_FIELD] . "
";
				$insert_new_field_names .= "'\$$field_name', ";
			}
			//create the code for this field
			$public_fields .= $this->generateFieldCode ( $field_properties, $is_primary_field );

		}
		$constructor = "";
		$foreign_class_getters = "";
		if (isset ( $primary_key_field )) {
			$constructor = $this->generateConstructor ( $primary_key_field, $primary_key_type );

			$foreign_class_getters = $this->generateForeignClassGetters ( $primary_key_field );
		}

		$insert_new_field_names = substr ( $insert_new_field_names, 0, - 2 );
		$insert_new_field_names_no_quotes = str_ireplace("'", "", $insert_new_field_names);
		$insert_new_field_names_no_cash = str_ireplace("$", "", $insert_new_field_names_no_quotes);
		
		$globalInsertFunction = "
		
if(!function_exists('insertNew$this->class_name')){
	/**
	* Inserts a new $this->class_name
	$insert_new_comments* @return int id of new $this->class_name
	*/
	function insertNew$this->class_name($insert_new_field_names_no_quotes){
		global \$" . $this->database . "_connection;
		\$sql = \"INSERT INTO $this->table_name ($insert_new_field_names_no_cash) VALUES ($insert_new_field_names);\";
		mysql_query ( \$sql, \$" . $this->database . "_connection ) or die(mysql_error( \$" . $this->database . "_connection ));
		return mysql_insert_id(\$" . $this->database . "_connection);
	}
}
";

$header = "<?php
if(!class_exists('$this->class_name', false)){

/**
* Generated by Brent Rossen's MySQL Ajax Database Access Layer
*/
require_once dirname(__FILE__).'/../Connection.php';
$requirements
//makes sure the Functions.php set of global functions are available
if(!function_exists('getClassProperties')){
	require_once dirname(__FILE__).'/../Controller/Functions.php';
}

$globalInsertFunction

/**
* Class of type $this->class_name
*/
class $this->class_name extends DBElement {
";

$staticClassGetter = "/**
	* Gets an array of all of the $this->class_name from the database
	* @param string \$OrderBy Field(s) to order by. If multiple fields, separate by a comma.
	* @param string \$direction ASC (ascending) or DESC (descending)
	* @return array of $this->class_name
	*/
	public static function get" . $this->class_name . "s(\$OrderBy = NULL, \$direction = 'DESC'){
		global \$" . $this->database . "_connection;
		\$" . $this->class_name . "s = array();
		if(\$OrderBy != NULL){
			\$sql = \"SELECT * FROM $this->table_name ORDER BY \$OrderBy \$direction\";
		}
		else{
			\$sql = \"SELECT * FROM $this->table_name\";
		}
		
		\$result = mysql_query(\$sql, \$" . $this->database . "_connection);
		while(\$row = mysql_fetch_array(\$result)){
			array_push(\$" . $this->class_name . "s, $this->class_name::constructFrom_" . $this->table_name . "_row(\$row));
		}
		return \$" . $this->class_name . "s;
	}
";
$staticJavascriptObjectsInstantiator = "
	/**
	* Instantiates the javascript objects by
	* printing out javascript that generates
	* a javascript object for each of the given PHP objects.
	* You must call this before you can use the print_delete_button
	*
	* @param array " . $this->class_name . "s The array of " . $this->class_name . " objects to instantiate as Javascript objects
	*/
	public static function instantiate_javascript_objects($" . $this->class_name . "s){
		?><script type=\"text/javascript\">
		<?php
		foreach ( $" . $this->class_name . "s as $" . $this->class_name . " ) {
			?>
		var " . $this->class_name . "<?php echo $" . $this->class_name . "->primary_key_value; ?> = new " . $this->class_name . "(<?php
			echo json_encode ( $" . $this->class_name . " );
			?>);
		<?php
		}
		?>
		</script><?php
	}
";

$dynamicJavascriptObjectInstantiator = "
	/**
	* Instantiates this PHP object as a javascript object by
	* printing out javascript that generates
	* a javascript object for this PHP object.
	* You must intantiate the javascript object before you can use the print_delete_button
	*
	*/
	public function instantiate_javascript_object(){
		?><script type=\"text/javascript\">
		var " . $this->class_name . "<?php echo \$this->primary_key_value; ?> = new " . $this->class_name . "(<?php
			echo json_encode ( \$this );
			?>);
		</script><?php
	}
";
$print_delete_button = "
/**
* Prints a delete button for this object.
* Requires that you have instantiated the javascript for this object.
* @param bool \$require_confirmation Should we ask the user to confirm their choice of deleting an object? Prevents accidental button presses.
* @param string \$on_complete The function to call when the delete is completed
*/
public function print_delete_button(\$require_confirmation, \$on_complete){
?>
<button class='deleteButton ".$this->class_name."DeleteButton' title=\"Delete this $this->class_name\" onclick=\"
		<?php
		if(\$require_confirmation){
		?>	
		var answer = confirm('Are you sure you want to delete this $this->class_name?');
		if (!answer) {
			return false;
		}
		<?php } ?>
		$this->class_name<?php echo \$this->primary_key_value; ?>.deleteMeFromDatabase(<?php echo \$on_complete ?>);
		return false; //prevents full page refresh on error
		\">Delete</button>
<?php
}
	";

		$staticClassDropdown = "
	/**
	* Takes an array of $this->class_name and constructs a dropdown list
	* Calls the javascript callback function
	* @param Array of $this->class_name \$" . $this->class_name . "s The list of " . $this->class_name . "s to display in the dropdown
	* @param string \$display_field The field of the table to display
	* @param string \$selected_value The value of the currently selected option in the dropdown
	* @param string callback The javascript function to call on change
	*/
	public static function print_" . $this->class_name . "_dropdown(\$" . $this->class_name . "s, \$display_field, \$selected_value, \$javascript_callback){
		?>
		<select onchange='<?php echo \$javascript_callback ?>(this.value)' class='" . $this->class_name . "_dropdown' title='Select a $this->class_name'>
		<option value=null>Select $this->class_title</option>
		<?php
		/* @var \${$this->class_name} $this->class_name */ 
		foreach (\$" . $this->class_name . "s as \$$this->class_name){
			?><option value='<?php echo \$" . $this->class_name . "->primary_key_value; ?>' <?php if(\$selected_value == \$" . $this->class_name . "->primary_key_value){ ?>selected='selected'<?php } ?>><?php echo \$" . $this->class_name . "->\$display_field; ?></option><?php 
		}
		?>
		</select>
		<?php
	}
	";
		$autocompleteDropdown = <<< HereDoc
	/**
	* Takes an array of {$this->class_name} and constructs an autocomplete field
	* The autocomplete will use regular expressions to find exact matches within the \$display_field
	* Calls the javascript callback function
	* @param Array of {$this->class_name} \${$this->class_name}s The list of {$this->class_name}s to use as autocomplete options
	* @param string \$display_field The field of the table to display
	* @param string \$selected_value The value of the currently selected option in the autocomplete (such as the primary key id)
	* @param string callback The javascript function to call on change
	*/
	public static function print_{$this->class_name}_autocomplete(\${$this->class_name}s, \$display_field, \$selected_value, \$javascript_callback){
		\${$this->class_name}AutocompleteInstructions = "Select a {$this->class_name}";
		?>
		<script type="text/javascript">	
			//Array of {$this->class_name} options, values
			var option{$this->class_name}s = [<?php 
			\$last_item = end(\${$this->class_name}s); 
			foreach(\${$this->class_name}s as \${$this->class_name}){
				echo '["' . \${$this->class_name}->\$display_field . '","' . \${$this->class_name}->primary_key_value .  '"]';
				if(\${$this->class_name}->\$display_field != \$last_item->\$display_field){
					echo ",";
				}
			}?>];
			
			//Array of {$this->class_name} names
			var option{$this->class_name}sNames = [<?php 
			\$last_item = end(\${$this->class_name}s); 
			foreach(\${$this->class_name}s as \${$this->class_name}){
				echo '"' . \${$this->class_name}->\$display_field . '"';
				if(\${$this->class_name}->\$display_field != \$last_item->\$display_field){
					echo ",";
				}
			}?>];
			
			//gets the value of the input, and finds that value in the list of ids
			function callJavascriptWith{$this->class_name}Value(){
				for(var i = 0; i < option{$this->class_name}s.length; i++){
					if(option{$this->class_name}s[i][0] == $("#{$this->class_name}AutocompleteInput").val()){
						<?php echo \$javascript_callback ?>(option{$this->class_name}s[i][1]);
						$("#{$this->class_name}AutocompleteInput").removeClass("ui-state-error");
						$("#{$this->class_name}AutocompleteInput").attr("title", "The {$this->class_name} '" + $("#{$this->class_name}AutocompleteInput").val() + "' is selected.");	
						
						return;
					}
				}
				//no existing item selected, null out the current value
				<?php echo \$javascript_callback ?>(null);
				
				//insert the new item
				$("#{$this->class_name}AutocompleteInput").addClass("ui-state-error");
				$("#{$this->class_name}AutocompleteInput").attr("title", "The {$this->class_name} '" + $("#{$this->class_name}AutocompleteInput").val() + "' does not exist.");	
			}
			
			function caretAutocompleteItems(){
				var currentAutocompleteValue = $("#{$this->class_name}AutocompleteInput").val();
				//make the first letter uppercase
				var currentAutocompleteValueFirstUpper = currentAutocompleteValue.substr(0, 1).toUpperCase() + currentAutocompleteValue.substr(1);
				//make the first letter lowercase
				var currentAutocompleteValueFirstLower = currentAutocompleteValue.substr(0, 1).toLowerCase() + currentAutocompleteValue.substr(1);
				
				$(".ui-menu-item > a").each(function(){
					$(this).html(
   						$(this).html().replace(currentAutocompleteValueFirstLower,'<span class="ui-state-highlight">'+currentAutocompleteValueFirstLower+'</span>')
  					);
					$(this).html(
   						$(this).html().replace(currentAutocompleteValueFirstUpper,'<span class="ui-state-highlight">'+currentAutocompleteValueFirstUpper+'</span>')
  					);
				});
			}
			
			//runs setup on the autocomplete
			$(function (){
				$("#{$this->class_name}AutocompleteInput").autocomplete({
					source: option{$this->class_name}sNames,
					delay: 0,
					minLength: 0,
					close: callJavascriptWith{$this->class_name}Value
				});
			});	
		</script>
		
		<input type="text" 
			id="{$this->class_name}AutocompleteInput"
			class='{$this->class_name}_autocomplete' 
			onblur="
				if(this.value == ''){
					this.value = 'Select a {$this->class_name}';
				}
				else{
					callJavascriptWith{$this->class_name}Value();
				}"
			onkeyup="
				callJavascriptWith{$this->class_name}Value();
				caretAutocompleteItems();
			"
			title="Type here to select a {$this->class_name}"
			value="Select a {$this->class_name}"
			onfocus="
				if(this.value == 'Select a {$this->class_name}'){
					this.value = '';
				}
			"
		/>
		<?php
	}
HereDoc;

		$staticClassDropdown .= $autocompleteDropdown;

		
		/* GENERATE THE CODE FOR THE STATIC CLASS ADDER */
		//generate the list of foreign tables I point to as a field list for requesting display fields
		$display_field_args = "";
		//parameter comments for the foreign display fields
		$param_comments = "";
		//the code that will print the dropdowns for the adder
		$dropdownPrinters = "";
		foreach ( $this->foreign_tables as $field => $foreign_table ) {
			if($field != "pointToMe" && $foreign_table != ""){
				$className = $this->formatClassName ( $foreign_table );
				$display_field_args .= ", \${$className}_display_field = NULL";
				$param_comments = "
	* @param string \${$className}_display_field Select the field to display in the dropdown for selection, use the class constants, for example {$className}::FIELD_ID_name. Leave null to use a blank field and type in the {$className} primary key id.";
				$dropdownPrinters .= "
			//check if the current property being evaluated is for this field
			//and if the \${$className}_display_field is defined
			//if they are, print the dropdown for it
			if(\$prop == '$field' && \${$className}_display_field != NULL){
				
				//get the dropdown based on the given field
				\${$className}s = {$className}::get{$className}s(\${$className}_display_field, 'ASC');
			
				//need to have a function to store the selected value
				//print the dropdown
				?><td>
				<script type='text/javascript'>
					function set{$className}AdderInputValue(id_value){
						$('#<?php echo \$prop ?>_adder_input').val(id_value);
					}
				</script>
				<input type='hidden' id='<?php echo \$prop ?>_adder_input' />
				<?php $className::print_{$className}_dropdown(\${$className}s, \${$className}_display_field, '', 'set{$className}AdderInputValue'); ?>
				</td><?php 
				\$dropdown_printed = true;
			}
		";
			}
		}
		
		$staticClassAdder = <<< BeginStaticClassAdder
	/**
	* Generates an interface to add to the table $this->table_name
	* There should only be one of these used per class type per page
	* Calls the javascript callback function
	* Note that this adder does not currently do any validation. If a field is not set, or a dropdown is not selected, default values will be used. This can be a problem for foreign key constraints and properties of non-string types.
	* @param string callback The javascript function to call on insert, returns the id of the new object
	* @param boolean print_field_inputs Tells the function whether or not to print html input fields for each of the class's fields, or to just print a button that adds with default values{$param_comments}
	*/
	public static function print_{$this->class_name}_add(\$javascript_callback, \$print_field_inputs = true{$display_field_args}){
		
		\$class_properties = getClassProperties('{$this->class_name}');
		\$class_prop_array = explode(', ', \$class_properties);
		?>
		<?php 
		if(\$print_field_inputs){
		?>
<table>
	<tr>				
	<?php		
		foreach(\$class_prop_array as \$prop){
			if(\$prop != ''){
			?><th><?php echo formatFieldNameToTitle(\$prop); ?></th><?php
			}
		} ?>
		</tr>
		<tr><?php	
		\$var_retriever_code = '';
		foreach(\$class_prop_array as \$prop){
			\$var_retriever_code .= "$('#\$prop'+'_adder_input').val(),";
			\$dropdown_printed = false;//holds whether or not this property is a foreign key, and is therefore covered by the dropdown code
			$dropdownPrinters
			if(!\$dropdown_printed && \$prop != ''){
				?><td><input type='text' class='" . $this->class_name . "_add_input_<?php echo \$prop ?>' id='<?php echo \$prop ?>_adder_input' title='Enter value for the <?php echo \$prop ?> here' /></td><?php 
			}
		} ?> 
		<td>
		<input type='button' class='{$this->class_name}_add_button' title='Add this {$this->class_title}' value='Add New {$this->class_title}' onclick="insertNew{$this->class_name}(<?php echo \$var_retriever_code ?><?php echo \$javascript_callback ?>);
		return false;
		" />
		</td>
	</tr>	
</table>	
<?php }
else{
	\$var_retriever_code = '';
	//print one null for each property
	foreach(\$class_prop_array as \$prop){
			\$var_retriever_code .= "'',";
	}
?>
	<input type='button' class='{$this->class_name}_add_button' title='Add this {$this->class_title}' value='Add New {$this->class_title}' onclick="insertNew{$this->class_name}(<?php echo \$var_retriever_code ?><?php echo \$javascript_callback ?>);
	return false;
	" />
<?php 
}
?>

		<?php
	}
BeginStaticClassAdder;

		$footer = "
}
}//end check for class existence
";
		return $header . $public_fields . $constructor . $staticClassGetter . $staticClassAdder . $staticClassDropdown . $staticJavascriptObjectsInstantiator . $dynamicJavascriptObjectInstantiator . $print_delete_button . $foreign_class_getters . $footer;
	}

	/**
	 * Takes the foreign_keys array and generates foreign class getters using that array
	 */
	protected function generateForeignClassGetters($primary_key_field) {
		$foreignClassGettersCode = "";
		$subObjectGettersCode = "";
		foreach ( $this->foreign_tables['pointToMe'] as $foreign_table ) {
			$className = $this->formatClassName ( $foreign_table );
			$subObjectGettersCode .= "
		foreach(\$this->get" . $className . "s() as \$$className){
			\$" . $className . "->instantiateSubObjects();
		}";
			$foreignClassGettersCode .= "
	/**
	* A private array of " . $className . "s for caching values retreived from the db
	* This array should not be accessed or edited directly. It is protected for serialization purposes
	* @var array
	*/
	public \$" . $className . "s = array();
	/**
	* @param string \$OrderBy Field(s) to order by. If multiple fields, separate by a comma.
	* @param string \$direction ASC (ascending) or DESC (descending)
	* @return array An array of " . $className . "s associated with this $this->class_name
	*/
	public function get" . $className . "s(\$OrderBy = NULL, \$direction = 'DESC'){
		//Check if " . $className . "s have been instantiated
		if(count(\$this->" . $className . "s) == 0){
			//retrieve all the " . $className . "s from the database
			if(\$OrderBy != NULL){
				\$sql = \"SELECT * FROM " . $foreign_table . " WHERE $primary_key_field='\$this->primary_key_value' ORDER BY \$OrderBy \$direction;\";
			}
			else{
				\$sql = \"SELECT * FROM " . $foreign_table . " WHERE $primary_key_field='\$this->primary_key_value';\";
			}
			
			\$result = mysql_query ( \$sql,  \$this->connection);

			while(\$row = mysql_fetch_array ( \$result )){
				array_push(\$this->" . $className . "s, $className::constructFrom_" . $foreign_table . "_row(\$row));
			}
		}
		return \$this->" . $className . "s;
	}

	/**
	* A fully instantiated private array of " . $className . "s for caching values retreived from the db
	* This array should not be accessed or edited directly. It is protected for serialization purposes
	* @var array
	*/
	public \$fullyInstantiated" . $className . "s = array();
	/**
	* @param string \$OrderBy Field(s) to order by. If multiple fields, separate by a comma.
	* @param string \$direction ASC (ascending) or DESC (descending)
	* @return array An array of fully instantiated " . $className . "s associated with this $this->class_name, fully instantiated means that all linked subobjects will also be retrieved
	*/
	public function getFullyInstantiated" . $className . "s(\$OrderBy = NULL, \$direction = 'DESC'){
		//Check if " . $className . "s have been instantiated
		if(count(\$this->fullyInstantiated" . $className . "s) == 0){
			//retrieve all the " . $className . "s from the database
			if(\$OrderBy != NULL){
				\$sql = \"SELECT * FROM " . $foreign_table . " WHERE $primary_key_field='\$this->primary_key_value' ORDER BY \$OrderBy \$direction;\";
			}
			else{
				\$sql = \"SELECT * FROM " . $foreign_table . " WHERE $primary_key_field='\$this->primary_key_value';\";
			}
			\$result = mysql_query ( \$sql,  \$this->connection);

			while(\$row = mysql_fetch_array ( \$result )){
				\$obj = $className::constructFrom_" . $foreign_table . "_row(\$row);
				\$obj->instantiateSubObjects();
				
				array_push(\$this->fullyInstantiated" . $className . "s, \$obj);
			}
		}
		return \$this->fullyInstantiated" . $className . "s;
	}
	
	/**
	* Inserts a new $className into this element as a foreign key object
	*
	* @uses To use insertNew$className: Instantiate an object of $className. Pass it the appropriate values using the PUBLIC FIELDS directly. 
	* DO NOT use the setters. Setters will attempt to access the database for an object that does not exist. 
	* Then pass the instantiated object to insertNew$className. The object is passed by reference, the primary key id will be set
	* by insertNew$className. Once you've inserted, you can use your instantiated object to access the database.
	*
	* @param $className \$new$className
	* @return int The new id of the inserted object
	*/
	public function insertNew$className(&\$new$className){
		if(\$new$className == NULL)
		{
			\$new$className = new $className();
		}
		return \$this->insert(\$new$className); //return the id of the new row
	}
	
	/**
	* Gets the parameters for the insertNew$className constructor
	*/
	public function get_insertNew" . $className . "_parameters()
	{
		return '$className';
	}
		
	/**
	* Instantiates and inserts a new $className into this element as a foreign key object
	*
	* @uses Pass the parameters of the new class to the function. For parameters you don't wish to instantiate, set them to NULL.
	* Don't be fooled by seeing no arguments for the function. This function uses a dynamic arguments list, 
	* it will read out whatever arguments you pass to it. 
	*
	* @return int The new id of the inserted object
	*/
	public function generateNew$className(){
		\$arg_list = func_get_args();
		\$arg_names = explode(',', \$this->get_generateNew" . $className . "_parameters());
		\$new$className = new $className();
		foreach(\$arg_list as \$key=>\$val){
			\$new{$className}->\$arg_names[\$key] = \$val;
		}

		return \$this->insert(\$new$className); //return the id of the new row
	}
	
	/**
	* Gets the parameters for the generateNew$className constructor
	*/
	public function get_generateNew" . $className . "_parameters()
	{
			//array of vars to skip
			\$skipArray = array(\"updated\");//updated is automatically inserted
			
			//don't want the DBElement vars either
			foreach (get_class_vars(\"DBElement\") as \$key => \$var)
			{
				array_push(\$skipArray, \$key);
			}
			//get the class' vars
			\$classVars = get_class_vars('$className');
			\$varsList = '';
			foreach(\$classVars as \$key => \$var)
			{
				//if it's not an arrary, and it's not in the skip array
				//and it's not the primary key field for the class
				//and it's not the primary key field for this class
				if(!is_array(\$var) && !in_array(\$key, \$skipArray) && \$key != \$classVars['primary_key_field'] && \$key != \$this->primary_key_field){
					\$varsList .= \$key . \", \";
				}
			}
			\$varsList = substr(\$varsList,0,-2);
		return \$varsList;
	}
";
		}
		if (isset ( $primary_key_field )) {
			$fullyInstantiatedConstructor = "
	/**
	* Constructs a fully instantiated " . $this->class_name . ". This means it cascades construction to all of it's sub objects
	* Warning: Instantiating classes this way can be very slow. It requires cascading sql calls.
	* @return a pointer to the class
	*/
	public static function constructFullyInstantiatedFrom_$primary_key_field(\$$primary_key_field) {
		\$me = $this->class_name::constructFrom_$primary_key_field(\$$primary_key_field);
		//call each of the getters
		\$me->instantiateSubObjects();
		return \$me;
	}
		
	/**
	* Gets the parameters for the constructFullyInstantiatedFrom_" . $primary_key_field . " constructor
	*/
	public function get_constructFullyInstantiatedFrom_" . $primary_key_field . "_parameters(){
		return '$primary_key_field';
	}
	
	/**
	* Instantiates all of this class's sub objects
	*/
	public function instantiateSubObjects(){
	$subObjectGettersCode
	}
";
		}

		//generates the single object foreign key getters
		foreach ( $this->foreign_tables as $field => $foreign_table ) {
			if($field != "pointToMe" && $foreign_table != ""){
				$className = $this->formatClassName ( $foreign_table );
				$foreignClassGettersCode .= "
	/**
	* A private " . $className . " for caching the object retreived from the db
	* This object should not be accessed or edited directly. It is protected for serialization purposes
	*/
	private \$" . $className . ";
				
	/**
	* @return $className A " . $className . " associated with this $this->class_name
	*/
	public function get" . $className . "(){
		//Check if " . $className . " has been instantiated
		if(!isset(\$this->" . $className . ")){
			//retrieve the " . $className . " from the database
			\$sql = \"SELECT * FROM " . $foreign_table . " WHERE $field='\$this->$field';\";
			\$result = mysql_query ( \$sql,  \$this->connection);
			if(mysql_num_rows(\$result) > 0){
				\$row = mysql_fetch_array ( \$result );
				\$this->" . $className . " = $className::constructFrom_" . $foreign_table . "_row(\$row);
			}
		}
		return \$this->" . $className . ";
	}";
			}

		}

		return $foreignClassGettersCode . $fullyInstantiatedConstructor;
	}

	/**
	 * Generates the constructor code
	 *
	 * @param string $primary_key_field
	 * @return string The constructor code
	 */
	protected function generateConstructor($primary_key_field, $primary_key_type) {
		$constructor = "

	public \$table_name = '$this->table_name';
	public \$primary_key_field = '$primary_key_field';

	/**
	 * Basic constructor for serialization
	 */
	public function __construct(){
		global \$" . $this->database . "_connection;
		\$this->className = '" . $this->class_name . "';
		\$this->connection = \$" . $this->database . "_connection;
	}
	
	/**
	* Constructs a $this->class_name using the given row $primary_key_field
	* @param " . $this->table_name . "_row \$" . $this->table_name . "_row A row of " . $this->table_name . "
	*/
	public static function constructFrom_" . $this->table_name . "_row(\$" . $this->table_name . "_row) {
		\$me = new $this->class_name();
		\$me->initializeVariables(\$" . $this->table_name . "_row);
		return \$me;
	}
			
	/**
	* Gets the parameters for the constructFrom_" . $this->table_name . "_row constructor
	*/
	public function get_constructFrom_" . $this->table_name . "_row_parameters(){
		return '" . $this->table_name . "_row';
	}
	";
		if (isset ( $primary_key_field )) {
			$constructor .= "
	/**
	* Constructs a $this->class_name using the primary key field $primary_key_field
	* if that is not a valid primary key, fails with a fatal error
	* @param $primary_key_type $primary_key_field The primary key field
	*/
	public static function constructFrom_$primary_key_field(\$$primary_key_field){	
		global \$" . $this->database . "_connection;
		\$$primary_key_field = mysql_real_escape_string(\$$primary_key_field);
		\$sql = \"SELECT * FROM " . $this->table_name . " WHERE $primary_key_field='\$$primary_key_field';\";
		\$result = mysql_query ( \$sql, \$" . $this->database . "_connection);
		\$row = mysql_fetch_array ( \$result );
		if(mysql_num_rows(\$result) != 1){
			throw new Exception(\"Invalid $primary_key_field = \$$primary_key_field parameter passed to $this->class_name constructFrom_$primary_key_field(\$$primary_key_field) constructor\");
		}
		return $this->class_name::constructFrom_" . $this->table_name . "_row(\$row);
	}
	
	/**
  * Attempts to construct a $this->class_name using the primary key field $primary_key_field
  * if that is not a valid primary key, returns null
  * @param $primary_key_type $primary_key_field The primary key field
  * @return the constructed object, or NULL
  */
  public static function tryConstructFrom_$primary_key_field(\$$primary_key_field){  
    global \$" . $this->database . "_connection;
    try{
      \$$primary_key_field = mysql_real_escape_string(\$$primary_key_field);
      \$sql = \"SELECT * FROM " . $this->table_name . " WHERE $primary_key_field='\$$primary_key_field';\";
      \$result = mysql_query ( \$sql, \$" . $this->database . "_connection);
      \$row = mysql_fetch_array ( \$result );
      if(mysql_num_rows(\$result) != 1){
        throw new Exception(\"Invalid $primary_key_field = \$$primary_key_field parameter passed to $this->class_name constructFrom_$primary_key_field(\$$primary_key_field) constructor\");
      }
      return $this->class_name::constructFrom_" . $this->table_name . "_row(\$row);
    }
    catch(Exception \$ex){
      return NULL;
    }
  }
  
	/**
	* Gets the parameters for the constructFrom_" . $primary_key_field . " constructor
	*/
	public function get_constructFrom_" . $primary_key_field . "_parameters(){
		return '$primary_key_field';
	}";
		}
		$constructor .= "
	/**
	* Initializes the variables using " . $this->table_name . "_row
	* @param " . $this->table_name . "_row \$row A row of " . $this->table_name . "
	*/
	protected function initializeVariables(\$row){
		//loop through each of the fields assigning their values
		foreach(\$row as \$field_name=>\$field_value){
			if (!is_int( \$field_name )) {//make sure we don't get the int keys from the row		
				\$this->\$field_name = \$field_value;
				if(\$field_name == \$this->primary_key_field){
					\$this->primary_key_value = \$this->\$field_name;
				}
			}
		}
	}
	";

		return $constructor;
	}

	/**
	 * @param string $field_type
	 * @param string $field_name
	 * @param string $parameter_value
	 * @return string the code for the parameter
	 */
	protected function generateFieldCode($field_properties, $is_primary_field) {
		$field_type = $field_properties [DATA_TYPE_FIELD];
		$comments = $field_properties [COMMENTS_FIELD];
		$field_name = $field_properties [FIELD_NAME_FIELD];

		$field_declaration = "
	
	/**
	* $field_name, $comments
	* Note, this value is public only so that it can be serialized. 
	* If you retrieved this object from the database, you should not get this value directly or assign to it directly.
	* Instead, use get_$field_name and set_$field_name.
	* The only time this field should be assigned directly is for instantiation to pass to an insertNew$this->class_name.
	* @var $field_type \$$field_name
	*/
	public \$$field_name;
	
	/**
	 * Constant for field identifier from {$this->table_name} table
	 * @var string FIELD_ID_".strtoupper($field_name)."
	 */
	const FIELD_ID_".strtoupper($field_name)." = '$field_name';
	";
	
		$getter = "
	/**
	 * Gets $field_name, $comments
	 * @return $field_type
	 */
	public function get_$field_name(){
		return \$this->$field_name;
	}
	
	/**
	* Gets the properties of $field_name
	*/
	public function get_" . $field_name . "_properties(){
		return new FieldProperties(" . var_export ( $field_properties, true ) . ");
	}
	
	";
		if (! $is_primary_field) {
			$setter = "
	/**
	 * Sets $field_name, $comments
	 * This method updates the database	using optimistic concurrency: If the value of the field has changed since the $this->class_name's $field_name was retrieved from the database, the update will fail.
	 * @param $field_type \$previous_$field_name The old value for $field_name
	 * @param $field_type \$new_$field_name The new value for $field_name 
	 * @return boolean Update success or failure
	 */
	public function set_$field_name(\$previous_$field_name, \$new_$field_name){
		//check to see if the previous value is equal to the current value, then we know $field_name has not been altered since this object's instantiation
		//second condition makes it so that a passed in null value is equal to a blank
		if(\$this->$field_name == \$previous_$field_name || (\$previous_$field_name == 'null' && \$this->$field_name == '')){
			//they are the same, update
			\$this->$field_name = \$new_$field_name;
			\$this->update('$field_name',\$new_$field_name);
			return TRUE;
		}
		//update failed
		return FALSE;
	}
	
	/**
	* Gets the parameters for the $field_name setter
	*/
	public function get_set_" . $field_name . "_parameters(){
		return 'previous_$field_name, new_$field_name';
	}";
	if ($field_type == "TextArea") {
				$setter .= "
	/**
	* Gets a html TextArea input field for setting $field_name
	*/
	public function print_" . $field_name . "_input_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
		echo \"<TextArea
			rows='20'
			cols='65'
   			id='{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Type here to edit this ".formatFieldNameToReadable($field_name).".'
			field_name = '{$field_name}';
			onblur='set{$this->class_name}{$field_name}{\$this->primary_key_value}(this.value);'
		>\$this->$field_name</TextArea>\";
	}
	
	/**
	* Gets a html TextArea wysiwyg field for setting $field_name
	* The wysiwyg feature can have a problem in Firefox if loaded via an ajax call. It should work fine in all browsers if loaded directly.
	*/
	public function print_" . $field_name . "_wysiwyg_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
	?>
		<script type=\"text/javascript\">
			$(
				function (){
					$('#" . $field_name . "_text_area_<?php echo \$this->primary_key_value; ?>').htmlarea();
				}
			);
		</script>
	<?php
		echo \"<TextArea
			rows='20'
			cols='65'
			id='" . $field_name . "_text_area_\" . \$this->primary_key_value . \"'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Type here to edit this ".formatFieldNameToReadable($field_name).".'
			field_name = '{$field_name}';
			onblur='set{$this->class_name}{$field_name}{\$this->primary_key_value}(this.value);'
		>\$this->$field_name</TextArea>\";
		?>
		<button 
			id='{$this->class_name}{$field_name}<?php echo \$this->primary_key_value ?>_input_setter'
			field_name = '{$field_name}';
			
			title='Click here to save this ".formatFieldNameToReadable($field_name).".'

			style='background:\"white\"'
			
			onclick=\"
				set{$this->class_name}{$field_name}<?php echo \$this->primary_key_value ?>($('#" . $field_name . "_text_area_<?php echo \$this->primary_key_value ?>').htmlarea('toHtmlString'));
		\">Save</button>
		<?php
	}
	
	";
			} else if ($field_type == "boolean") {
				$setter .= "
	/**
	* Gets a html checkbox input field for setting $field_name
	*/
	public function print_" . $field_name . "_input_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
		echo \"<input
			type='checkbox'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Click here to toggle ".formatFieldNameToReadable($field_name).". Checked is yes, unchecked is no.'
			
			id='{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			field_name = '{$field_name}';
			
			onchange='
			if(this.checked){
				set{$this->class_name}{$field_name}{\$this->primary_key_value}(1);
			} else {
				set{$this->class_name}{$field_name}{\$this->primary_key_value}(0);			
			}'
			\";
			if(\$this->$field_name){
				echo \" checked='checked' \";
			}
		echo \"/>\";
	}
	
	";
			} else if ($field_type == "float") {
				$setter .= "
	/**
	* Gets a html text input field for setting $field_name
	* checks to make sure a number is submitted
	*/
	public function print_" . $field_name . "_input_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
		echo \"<input
			type='text'
			size='3'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Type here to edit this ".formatFieldNameToReadable($field_name).".'
			
			id='{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			field_name = '{$field_name}';
			
			onblur='
				if(isNaN(this.value)){
					alert(\\\"$field_name must be a number (float)\\\");
					this.value = $this->class_name\" . \$this->primary_key_value . \".$field_name;
				} 
				else {
					set{$this->class_name}{$field_name}{\$this->primary_key_value}(this.value);
				}'
			value='\$this->$field_name'
		/>\";
	}
	";
			} else if ($field_type == "integer") {
				$setter .= "
	/**
	* Gets a html text input field for setting $field_name
	* checks to make sure a number is submitted
	*/
	public function print_" . $field_name . "_input_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
		echo \"<input
			type='text'
			size='2'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Type here to edit this ".formatFieldNameToReadable($field_name).".'
			
			id='{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			field_name = '{$field_name}';
			
			onblur='
				if(isNaN(this.value)){
					alert(\\\"$field_name must be a number (integer)\\\");
					this.value = $this->class_name\" . \$this->primary_key_value . \".$field_name;
				} 
				else {
					set{$this->class_name}{$field_name}{\$this->primary_key_value}(this.value);
				}'
			value='\$this->$field_name'
		/>\";
	}
	";
			}
			else if ($field_type == "Date") {
				$setter .= "
	/**
	* Gets a date field for setting $field_name
	* Uses the jQuery ui Datepicker interface
	*/
	public function print_" . $field_name . "_input_setter(){	
		echo \"".$this->getColorChangingSetterJS($field_name)."\";			
		?>
		<input
			type='text'
			size='8'
			class='datepicker<?php echo \$this->primary_key_value; ?> " . $this->class_name . "_" . $field_name . "_input_setter ui-state-default ui-corner-all'
			title='Click to select a date for ".formatFieldNameToReadable($field_name)."'
			
			id='{$this->class_name}{$field_name}<?php echo \$this->primary_key_value ?>_input_setter'
			field_name = '{$field_name}';
			
			value='<?php 
				if(\$this->$field_name != null){
					echo date(\"m/d/Y\", strtotime(\$this->$field_name));
				}
				?>'/>
				
			<script	type='text/javascript'>
				$(function() {
					$('.datepicker<?php echo \$this->primary_key_value; ?>').datepicker({
							onSelect: function(dateText, inst) {  
								var myDate = $.datepicker.formatDate('yy-mm-dd', new Date(dateText));
								set{$this->class_name}{$field_name}<?php echo \$this->primary_key_value ?>(myDate); 
							 }
					});
				});
			</script>
				
				<?php
				
	}
				";
			}
			else{
				//default setter
				$setter .= "
	/**
	* Gets a html text input field for setting $field_name
	*/
	public function print_" . $field_name . "_input_setter(){
		echo \"".$this->getColorChangingSetterJS($field_name)."\";
		echo \"<input
			type='text'
			class='" . $this->class_name . "_" . $field_name . "_input_setter'
			title='Type here to edit this ".formatFieldNameToReadable($field_name).".'
			id='{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			field_name = '{$field_name}';
			onblur='set{$this->class_name}{$field_name}{\$this->primary_key_value}(this.value);'
			size='\" . strlen(\$this->$field_name) . \"'
			value='\" . htmlspecialchars ( \$this->$field_name, ENT_QUOTES, \"ISO-8859-1\", false ) . \"'
		/>\";
	}
	
	";

			}
		} else {
			$setter = "";
		}
		$static_class_array_getter = $this->generateStaticClassArrayGetter ( $field_name );
		return $field_declaration . $getter . $setter . $static_class_array_getter;
	}

	/**
	 * Gets the javascript code for changing the color of a field on update
	 * @param string $field_name
	 */
	private function getColorChangingSetterJS($field_name){
		$script = "
		<div style='position:absolute; display:none; background:#E8E8E8; opacity:0.8;' id='{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay'></div>
		<script type='text/javascript'>
		var {$this->class_name}{$field_name}{\$this->primary_key_value}color = 'white';
		
		function {$this->class_name}{$field_name}{\$this->primary_key_value}_updated(success){
			if(success){
				$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').css({background:'#55FF55'});
				$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').html('Saved');				
				setTimeout(set{$this->class_name}{$field_name}{\$this->primary_key_value}Normal, 1000); 
			}
			else{
				$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').css({background:'red'});
				$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').html('Error, try refreshing the page.');
			}
			updateSuccessful(success);
		}
		function set{$this->class_name}{$field_name}{\$this->primary_key_value}Normal(){
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').css({background:{$this->class_name}{$field_name}{\$this->primary_key_value}color});
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').hide();
			//re-enable the field
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').removeAttr('disabled');
		}
		function set{$this->class_name}{$field_name}{\$this->primary_key_value}(value){
			//set the $this->class_name $field_name
			
			$this->class_name\" . \$this->primary_key_value . \".set_$field_name(value, {$this->class_name}{$field_name}{\$this->primary_key_value}_updated);
			{$this->class_name}{$field_name}{\$this->primary_key_value}color = $('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').css('background-color');
			//change color to yellow
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').css({background:'#FFFFCC'});			
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').html('Saving...');
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').show();
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}SaverDisplay').position({
			  my: 'left top',
			  at: 'right top',
			  of: '#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter'
			});
			
			//disable this field so it can't be resubmitted multiple times
			$('#{$this->class_name}{$field_name}{\$this->primary_key_value}_input_setter').attr('disabled','disabled');
			
		}
		</script>
		";
		return $script;
	}
	
	/**
	 * Generates and returns the javascript code for the class
	 *
	 * @return string The javascript code for the class
	 */
	public function generateJavascriptClass() {
		$cgj = new ClassGeneratorJavascript ( $this->database, $this->table_name, $this->fields, $this->foreign_tables['pointToMe'], $this->ignore_class_name_reformatting );
		return $cgj->generateClassCode ();
	}

	/**
	 * Generate a static class getter
	 * @return string
	 */
	public function generateStaticClassArrayGetter($field_name) {

		$staticClassGetter = "
	/**
	* Gets an array of $this->class_name where $field_name = \$$field_name from the database
	* @param variable \$$field_name The value of the field to select by
	* @param string \$OrderBy Field(s) to order by. If multiple fields, separate by a comma.
	* @param string \$direction ASC (ascending) or DESC (descending)
	* @return array of $this->class_name
	*/
	public static function get" . $this->class_name . "s_by_$field_name(\$$field_name, \$OrderBy = NULL, \$direction = 'DESC'){
		global \$" . $this->database . "_connection;
		\$" . $this->class_name . "s = array();
		\$$field_name = mysql_real_escape_string(\$$field_name);
		if(\$OrderBy != NULL){
			\$sql = \"SELECT * FROM $this->table_name WHERE $field_name = '\$$field_name' ORDER BY \$OrderBy \$direction;\";
		}
		else{
			\$sql = \"SELECT * FROM $this->table_name WHERE $field_name = '\$$field_name'\";
		}
		
		\$result = mysql_query(\$sql, \$" . $this->database . "_connection);
		while(\$row = mysql_fetch_array(\$result)){
			array_push(\$" . $this->class_name . "s, $this->class_name::constructFrom_" . $this->table_name . "_row(\$row));
		}
		return \$" . $this->class_name . "s;
	}

";
		//parameters for static class getter
		$parameters_getter = "
	/**
	* Gets the parameters for get_" . $this->class_name . "s_by_$field_name(\$$field_name)
	*/
	public function get_get" . $this->class_name . "s_by_" . $field_name . "_parameters(){
		return '$field_name';
	}";
		return $staticClassGetter . $parameters_getter;
	}

}

?>
